package com.example.template;

import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @Classname TemplateEngineList
 * @Description TODO
 * @Date 2023/4/28 10:59
 * @Created by XiongXiong
 * @Author: X.I.O
 */
public class TemplateEngineList {

    private static final String VAR_PATTERN = "\\$\\{([a-zA-Z_][a-zA-Z0-9_]*)\\}";
   // private static final String IF_PATTERN = "<if\\s+test=\"([a-zA-Z_][a-zA-Z0-9_]*?)\">(.*?)</if>";
   private static final String IF_PATTERN  = "<if\\s+test=\"([^<\"]*?)\">(.*?)</if>";

    private static final String EACH_PATTERN = "<each\\s+var=\"([a-zA-Z_][a-zA-Z0-9_]*)\"\\s+in=\"([a-zA-Z_][a-zA-Z0-9_.]*)\">(.*?)</each>";
    private static final Pattern VAR_REGEX = Pattern.compile(VAR_PATTERN);
    private static final Pattern IF_REGEX = Pattern.compile(IF_PATTERN, Pattern.DOTALL);
    private static final Pattern EACH_REGEX = Pattern.compile(EACH_PATTERN, Pattern.DOTALL);

    // 解析模板并返回最终的文本
    public static String render(String template, Map<String, Object> context) {
        //先渲染所有变量
       // String output = renderVars(template, context);
        String output=template;
        //渲染if
        output = renderIf(output, context);
        //渲染循环
        output = renderEach(output, context);
        return output;
    }

    // 渲染变量
    private static String renderVars(String template, Map<String, Object> context) {
        StringBuffer result = new StringBuffer();
        Matcher matcher = VAR_REGEX.matcher(template);
        while (matcher.find()) {
            String key = matcher.group(1);
            String value = context.getOrDefault(key, "").toString();
            matcher.appendReplacement(result, value);
        }
        matcher.appendTail(result);
        return result.toString();
    }

    // 渲染if语句
    private static String renderIf(String template, Map<String, Object> context) {
        Matcher matcher = IF_REGEX.matcher(template);

        while (matcher.find()) {
            System.out.println(matcher.group(1));
            System.out.println(matcher.group(2));
            String booleanValues =matcher.group(1);
            boolean condition = Boolean.parseBoolean(context.getOrDefault(matcher.group(1), "false").toString());
            String content = matcher.group(2);
            String replacement = condition ? content : "";
            template = template.replace(matcher.group(0), replacement);
        }
        System.out.println("结果:"+template);
        return template;
    }

    // 渲染each语句
    private static String renderEach(String template, Map<String, Object> context) {
        Matcher matcher = EACH_REGEX.matcher(template);

        while (matcher.find()) {
            String varName = matcher.group(1);
            String collectionExpr = matcher.group(2);
            String content = matcher.group(3);
            System.out.println("varName:"+varName);
            System.out.println("content:"+content);
            System.out.println("collectionExpr:"+collectionExpr);
            Collection<?> collection = getCollection(collectionExpr, context);
            StringBuilder result = new StringBuilder();

            for (Object item : collection) {
                Map<String, Object> loopContext = new HashMap<>(context);
                loopContext.put(varName, item);
                result.append(renderVars(content, loopContext));
            }

            template = template.replace(matcher.group(0), result.toString());
        }

        return template;
    }

    // 获取集合表达式对应的集合
    private static Collection<?> getCollection(String collectionExpr, Map<String, Object> context) {
        String[] parts = collectionExpr.split("\\.");

        if (parts.length == 1) {
            return (Collection<?>) context.getOrDefault(parts[0], "");
        } else {
            Object obj = context.getOrDefault(parts[0], "");
            for (int i = 1; i < parts.length; i++) {
                obj = getValue(obj, parts[i]);
            }
            return (Collection<?>) obj;
        }
    }

    // 获取对象中指定属性名或方法名对应的值
    private static Object getValue(Object obj, String name) {
        try {
            Class<?> clazz = obj.getClass();
            String methodName = "get" + name.substring(0, 1).toUpperCase() + name.substring(1);
            return clazz.getMethod(methodName).invoke(obj);
        } catch (Exception e) {
            return null;
        }
    }

    public static void main(String[] args) {
        String template =
                "<if test=\"isAdmin && isAdmin\">" +
                "   <each var=\"item\" in=\"items\">\n" +
                "       <li>${item}</li>\n" +
                "   </each>\n" +
                "</if>";
        List<String> items = Arrays.asList("apple", "banana", "cherry");
        Map<String, Object> context = new HashMap<>();
        context.put("items", items);
        context.put("isAdmin",true);
        String output = TemplateEngineList.render(template, context);
        System.out.println(output);

        /**
         * 这个正则表达式的含义如下：
         *
         * <([a-zA-Z][a-zA-Z0-9]*)：匹配开始标签，其中[a-zA-Z][a-zA-Z0-9]*表示标签名必须以字母开头，后面可以跟任意字母或数字。
         * (\\s+[a-zA-Z][a-zA-Z0-9]*\\s*=\\s*(?:\"[^\"]*\"|'[^']*'))*：匹配开始标签中的所有属性，其中\\s+表示属性名前可能有一些空格，[a-zA-Z][a-zA-Z0-9]*表示属性名必须以字母开头，后面可以跟任意字母或数字，\\s*=\\s*表示属性名和属性值之间可能会有一些空格，(?:\"[^\"]*\"|'[^']*')表示属性值可以用双引号或单引号括起来，中间可以包含任意字符，但不能包含对应的引号。
         * \\s*>：匹配开始标签的结束符号，其中\\s*表示可能会有一些空格。
         * (.*?)：匹配标签包裹的内容，其中.*?表示非贪婪匹配任意字符。
         * </\\1>：匹配结束标签，其中\\1表示引用第一个捕获组中的标签名。
         */
        String input = "<p class=\"author\">Author: Alice</p><img src=\"image.png\" alt=\"A picture\">";
        String pattern = "<([a-zA-Z][a-zA-Z0-9]*)(\\s+[a-zA-Z][a-zA-Z0-9]*\\s*=\\s*(?:\"[^\"]*\"|'[^']*'))*\\s*>(.*?)</\\1>";
        Pattern regex = Pattern.compile(pattern, Pattern.DOTALL);
        Matcher matcher = regex.matcher(input);

        while (matcher.find()) {
            String tag = matcher.group(1);
            String attrs = matcher.group(2);
            String content = matcher.group(3);
            System.out.println("tag: " + tag);
            System.out.println("attrs: " + attrs);
            System.out.println("content: " + content);
        }
    }
}
